import tkinter as tk
from tkinter import ttk, messagebox

import numpy as np
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.pyplot as plt
import matplotlib
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.action_chains import ActionChains
from bs4 import BeautifulSoup


# 爬虫类
class Crawler:
    def __init__(self):
        # 设置中文字体
        matplotlib.rcParams['font.family'] = 'SimHei'  # 设置为黑体
        matplotlib.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题

        # 配置ChromeOptions，设置detach为True，使浏览器窗口不会自动关闭
        self.chrome_options = webdriver.ChromeOptions()
        self.chrome_options.add_experimental_option("detach", True)

        # 指定chromedriver的实际路径
        self.chromedriver_path = r'C:\Program Files\Google\Chrome\Application\chromedriver.exe'
        self.service = Service(executable_path=self.chromedriver_path)

        # 创建Chrome浏览器实例相关属性初始化
        self.driver = None
        self.all_data = []

    def start_crawling(self, selected_month):
        # 启动 Chrome 浏览器
        self.driver = webdriver.Chrome(options=self.chrome_options, service=self.service)
        self.driver.get("https://www.dongchedi.com/sales")  # 打开懂车帝页面

        body_element = self.driver.find_element(By.TAG_NAME, "body")

        # 向下滚动界面
        def down():
            for i in range(0, 300):
                body_element.send_keys(Keys.DOWN)

        def get_html_from_url():
            html = self.driver.page_source
            return html

        def parse_single_html(html):
            hash_sale = {}
            soup = BeautifulSoup(html, 'html.parser')
            cars = (soup.find("div", class_="jsx-495096444 tw-col-span-5")).find_all("li", class_="list_item__3gOKl")
            datas = []
            for i in range(0, 50):
                car = cars[i]
                car_name = car.find("a", class_="tw-font-semibold").get_text()
                car_type = car.find("span", class_="tw-text-12 tw-text-color-gray-700 tw-ml-6").get_text()
                car_sale_price = car.find("p", class_="tw-leading-22 tw-text-color-red-500 tw-font-semibold").get_text()
                car_sale_count = car.find("p", class_="tw-text-18 tw-font-semibold tw-leading-28").get_text()
                car_sale_count = car_sale_count.replace(",", "")
                car_com = car_type.split("/")[0]
                if car_com not in hash_sale:
                    hash_sale[car_com] = int(car_sale_count)
                else:
                    hash_sale[car_com] += int(car_sale_count)
                datas.append({
                    "排名": (i + 1),
                    "车名": car_name,
                    "品牌/车型": car_type,
                    "价格": car_sale_price,
                    "本月销量": car_sale_count
                })
            self.driver.quit()
            return datas

        # 点击切换月份
        def click(name):
            button = self.driver.find_element(By.CSS_SELECTOR, ".tw-relative.more_item__2UARp")
            actions = ActionChains(self.driver)
            actions.move_to_element(button)
            actions.perform()  # 执行构建好的鼠标动作链，实现鼠标悬停效果
            button2 = self.driver.find_elements(By.CSS_SELECTOR, "a.more_option___6xpL")
            for b in button2:
                element_text = b.text  # 获取元素的文本内容
                if element_text == name:
                    b.click()  # 如果文本内容符合要求，点击该按钮

        click(selected_month)
        down()
        html = get_html_from_url()
        datas = parse_single_html(html)
        self.all_data.extend(datas)
        return self.all_data

# 图表生成类
class Chart:
    def __init__(self, chart_display_frame):
        self.chart_display_frame = chart_display_frame

    def plot_pie_chart(self, all_data):
        # 清除之前的图表
        for widget in self.chart_display_frame.winfo_children():
            widget.destroy()

        # 数据处理：获取所有车企的销量
        brand_sales = {}
        for data in all_data:
            brand = data["品牌/车型"].split("/")[0]  # 车企名称
            sale_count = int(data["本月销量"])  # 销量
            if brand in brand_sales:
                brand_sales[brand] += sale_count
            else:
                brand_sales[brand] = sale_count

        # 提取车企和销量数据
        brands = list(brand_sales.keys())
        sales = list(brand_sales.values())

        # 按销量降序排列
        sorted_data = sorted(zip(brands, sales), key=lambda x: x[1], reverse=True)
        brands, sales = zip(*sorted_data)

        # 创建扇形图
        fig = plt.figure(figsize=(12, 12))  # 设定图表大小
        plt.pie(sales, labels=brands, autopct='%1.1f%%', startangle=140)
        plt.title('各车企汽车销量扇形图')

        # 在Tkinter中显示图表
        canvas_agg = FigureCanvasTkAgg(fig, master=self.chart_display_frame)
        canvas_agg.get_tk_widget().pack(pady=10, fill=tk.BOTH, expand=True)
        canvas_agg.draw()

    def plot_bar_chart(self, all_data):
        # 清除之前的图表
        for widget in self.chart_display_frame.winfo_children():
            widget.destroy()

        # 数据处理：获取所有车企的销量
        brand_sales = {}
        for data in all_data:
            brand = data["品牌/车型"].split("/")[0]  # 车企名称
            sale_count = int(data["本月销量"])  # 销量
            if brand in brand_sales:
                brand_sales[brand] += sale_count
            else:
                brand_sales[brand] = sale_count

        # 提取车企和销量数据
        brands = list(brand_sales.keys())
        sales = list(brand_sales.values())

        # 按销量降序排列
        sorted_data = sorted(zip(brands, sales), key=lambda x: x[1])
        brands, sales = zip(*sorted_data)

        # 创建水平柱状图
        fig, ax = plt.subplots(figsize=(10, 6))  # 设定图表大小
        colors = ['skyblue'] * (len(brands) - 5) + ['red'] * 5  # 前五个为红色，其余为天蓝色
        bars = ax.barh(brands, sales, color=colors)  # 创建水平柱状图

        # 在柱子上方添加数值标签
        for bar in bars:
            width = bar.get_width()
            ax.text(width, bar.get_y() + bar.get_height() / 2, f'{width}', va='center', ha='left', fontsize=8)

        # 添加标签和标题
        ax.set_xlabel('销量')
        ax.set_ylabel('车企')
        ax.set_title('各车企汽车销量柱状图')

        # 在Tkinter中显示图表
        canvas_agg = FigureCanvasTkAgg(fig, master=self.chart_display_frame)
        canvas_agg.get_tk_widget().pack(pady=10, fill=tk.BOTH, expand=True)
        canvas_agg.draw()


# GUI类
class GUI:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("懂车帝销量数据爬虫")
        self.root.geometry("1000x1000")

        # 创建Tab控件
        self.tab_control = ttk.Notebook(self.root)
        self.tab_control.pack(expand=1, fill="both")

        # 创建控制面板
        self.control_panel = ttk.Frame(self.tab_control)
        self.tab_control.add(self.control_panel, text="控制面板")

        # 创建按钮面板
        self.button_panel = ttk.Frame(self.control_panel)
        self.button_panel.pack(side=tk.TOP, fill=tk.X)

        # 创建月份选择框
        self.selected_month_var = tk.StringVar(value="2024年11月")
        self.month_combobox = ttk.Combobox(self.button_panel, textvariable=self.selected_month_var, state="readonly", width=12)
        self.month_combobox["values"] = ["2024年11月", "2024年10月", "2024年09月", "2024年08月", "2024年07月", "2024年06月"]
        self.month_combobox.pack(side=tk.LEFT, padx=5, pady=10)

        # 创建图表展示框架相关代码提前
        self.chart_tab = ttk.Frame(self.tab_control)
        self.tab_control.add(self.chart_tab, text="图表展示")
        self.chart_display_frame = ttk.Frame(self.chart_tab)
        self.chart_display_frame.pack(fill=tk.BOTH, expand=True)

        # 实例化爬虫类和图表类，此时self.chart_display_frame已经存在
        self.crawler = Crawler()
        self.chart = Chart(self.chart_display_frame)

        # 爬虫按钮
        self.crawl_button = tk.Button(self.button_panel, text="开始爬取数据", command=self.start_crawling)
        self.crawl_button.pack(side=tk.LEFT, padx=5, pady=10)

        # 生成扇形图按钮
        self.generate_chart_button = tk.Button(self.button_panel, text="生成扇形图", command=self.generate_pie_chart)
        self.generate_chart_button.pack(side=tk.LEFT, padx=5, pady=10)

        # 生成柱状图按钮
        self.generate_bar_chart_button = tk.Button(self.button_panel, text="生成柱状图", command=self.generate_bar_chart)
        self.generate_bar_chart_button.pack(side=tk.LEFT, padx=5, pady=10)

        # 退出按钮
        self.exit_button = tk.Button(self.button_panel, text="退出", command=self.exit_program)
        self.exit_button.pack(side=tk.LEFT, padx=5, pady=10)

        # 创建数据表格（Treeview）
        self.treeview = ttk.Treeview(self.control_panel, columns=("排名", "车名", "品牌/车型", "价格", "本月销量"), show="headings")
        self.treeview.heading("排名", text="排名")
        self.treeview.heading("车名", text="车名")
        self.treeview.heading("品牌/车型", text="品牌/机型")
        self.treeview.heading("价格", text="价格")
        self.treeview.heading("本月销量", text="本月销量")
        self.treeview.pack(fill=tk.BOTH, expand=True)

    def start_crawling(self):
        selected_month = self.selected_month_var.get()
        all_data = self.crawler.start_crawling(selected_month)
        self.display_data(all_data)

    def display_data(self, datas):
        # 清空Treeview
        for row in self.treeview.get_children():
            self.treeview.delete(row)
        # 在Treeview中显示数据
        for data in datas:
            self.treeview.insert("", "end",
                                 values=(data["排名"], data["车名"], data["品牌/车型"], data["价格"], data["本月销量"]))

    def generate_pie_chart(self):
        data = self.crawler.all_data
        self.chart.plot_pie_chart(data)

    def generate_bar_chart(self):
        data = self.crawler.all_data
        self.chart.plot_bar_chart(data)

    def exit_program(self):
        self.root.quit()
        if self.crawler.driver:
            self.crawler.driver.quit()

    def run(self):
        self.root.mainloop()


if __name__ == "__main__":
    gui = GUI()
    gui.run()